//
// Created by hjie on 23-9-17.
//

#include "turn_server.h"
#include "data_buffer.h"
#include "define.h"
#include "message_process.h"
#include "stun_message.h"
#include "turn_server/thread_pool.h"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <memory>
#include <mutex>
#include <netinet/in.h>
#include <string>
#include "session.h"
#include <sys/socket.h>


TurnServer::TurnServer(LoopEvent *loop_event)
{
    m_config      = std::make_shared<StunConfig>();
    m_loop_event  = loop_event;
    m_udp_server  = nullptr;
    m_listen_port = 0;
}

TurnServer::~TurnServer()
{
    if (m_udp_server)
    {
        delete m_udp_server;
        m_udp_server = nullptr;
    }
}

int TurnServer::Listen(int port)
{

    m_process_ptr = std::make_shared<MessageProcess>(this);
    m_listen_port = port;
    m_udp_server = new UdpServer(m_loop_event);
    m_udp_server->SetUdpMessage(std::bind(&TurnServer::OnMessageThreadPool, this, std::placeholders::_1));
    m_udp_server->Listen(m_listen_port);

    m_thread_manager = std::make_shared<ThreadPoolManager>(4);

    m_thread_manager->Run();

    m_loop_event->PushTask([this](){

        OnTimer();
    });
    return true;
}

void TurnServer::OnTimer()
{
    m_process_ptr->CheckUdpServerExpire();
    m_loop_event->PushAfterTask(10 * 1000, [this](){


        OnTimer();
    });
    int64_t now_time = time(nullptr);
    for (auto iter = m_session_map.begin(); iter != m_session_map.end(); )
    {
        auto use_iter = iter++;
        if ((now_time - use_iter->second->m_latest_update_time) >= 120)
        {
            printf("remove session key : %s.\n", use_iter->first.c_str());
            m_session_map.erase(use_iter);
        }
    }
}

void TurnServer::OnMessageThreadPool(UdpPacket *packet)
{
    auto buffer_packet = std::make_shared<UdpBufferPacket>();

    buffer_packet->sai     = (struct sockaddr*)malloc(sizeof(struct sockaddr_in));
    buffer_packet->sai_len = sizeof(packet->sai); 

    memcpy(buffer_packet->sai, &packet->sai, buffer_packet->sai_len);

    uint32_t index = ((struct sockaddr_in*)buffer_packet->sai)->sin_addr.s_addr;

    std::string key = std::to_string(index) + ":" + std::to_string(((struct sockaddr_in*)buffer_packet->sai)->sin_port);
    // find session in map
    std::shared_ptr<Session> session;
    // session process message data
    auto iter = m_session_map.find(key);
    if (iter != m_session_map.end())
    {
        {
            std::lock_guard<std::mutex> lock(iter->second->m_mutx);
            buffer_packet->buffer = iter->second->m_data_buffer;
            iter->second->m_data_buffer->PushDataBuffer(packet->data, packet->length);
        }
        session = iter->second;
    }
    else
    {
        auto new_session   = std::make_shared<Session>(this);
        {
            std::lock_guard<std::mutex> lk(new_session->m_mutx);
            buffer_packet->buffer = new_session->m_data_buffer;
            new_session->m_data_buffer->PushDataBuffer(packet->data, packet->length);
        }
        printf("create new session key : %s.\n", key.c_str());
        session = new_session;
        m_session_map[key] = new_session;
    }
    m_thread_manager->PushTaskByIndex(index, [this, buffer_packet, session]() {

        session->ParserData(buffer_packet.get());
        free(buffer_packet->sai);
        buffer_packet->sai = nullptr;
        buffer_packet->buffer = nullptr;
    });
}

void TurnServer::ProcessStunMessage(UdpBufferPacket &packet, StunMessage &message)
{
    WriteBuffer buffer;
    std::vector<StunMessage> stun_message_vec;

    m_process_ptr->ProcessCommand(message, stun_message_vec);

    for (auto & data : stun_message_vec)
    {
        data.ComposeStunPacket(buffer);
    }
    if (buffer.Length() > 0)
    {
        m_udp_server->SendMsg(packet, buffer.Data(), buffer.Length());
    }
}